<?php
namespace bdhert\PhpLock;

/**
 * 锁服务
 * Class Lock
 * @package bdhert\PhpLock
 */
final class Lock{
    public $is_invalid = false; // 服务失效，是否降级判断

    const LOCK_TYPE_FILE  = 'file';
    const LOCK_TYPE_REDIS = 'redis';
    const LOCK_TYPE_SQL   = 'SQLLock';

    private $lock = null;
    private $lock_pond = [];
    private $support_locks = [
        self::LOCK_TYPE_FILE  => 'FileLock',
        self::LOCK_TYPE_REDIS => 'RedisLock',
        self::LOCK_TYPE_SQL   => 'SqlLock'
    ];

    private $param;

    public function __construct($type, $param = NULL){
        if(!empty($type)) {
            $this->createLock($type, $param);
        }
    }

    /**
     * 获取锁服务
     * @param $type
     * @param null $param
     */
    public function createLock($type, $param = NULL){
        if($class_name = $this->support_locks[$type] ?? NULL){
            $class = __NAMESPACE__ . "\\$class_name";

            if ($param) {
                $this->lock = new $class($param);
            } else {
                $this->lock = new $class();
            }
        }
    }

    /**
     * 获取锁
     * @param $key
     * @param int $timeout
     * @return bool
     */
    public function get($key, $timeout = ILock::EXPIRE){
        if(!$this->lock || !($this->lock instanceof ILock)) return false;

        try {
            $lock_res = $this->lock->get($key, $timeout);
            $this->lock_pond[$key] = $lock_res;
        }catch (\Exception $e){
            $this->is_invalid = true;
            return false;
        }

        return $lock_res;
    }

    /**
     * 释放锁
     * @param $key
     */
    public function release($key): void {
        if($this->lock && ($this->lock instanceof ILock)){
            try {
                /**
                 * 注意
                 * 一定要拿到锁才能释放
                 * 有的唯一逻辑的确可以不主动释放锁等待自动过期（也可以设置超时时间为0），但是会影响响应性能
                 */
                if(isset($this->lock_pond[$key]) && $this->lock_pond[$key]) $this->lock->release($key);
            }catch (\Exception $e){
                $this->is_invalid = true;
            }
        }
    }
}
